package fr.labri.macchiato.feature.localDatabase;

import java.util.List;

import fr.labri.macchiato.core.model.BusinessEntity;



/**
 * Class that builds all the query in order to retrieve data from the database.
 * 
 * @author Paul SARRAUTE
 * @author Matthieu FOUCAULT
 * @author Michelle AVOMO
 * 
 * 
 */
public class SparqlQueryFactory {

	private static SparqlQueryFactory instance = null;
	private SparqlQueryBuilder queryBuilder;

	private SparqlQueryFactory() {
		queryBuilder = new SparqlQueryBuilder();
	}

	/**
	 * 
	 * @return an instance of a QueryFactory
	 */
	public static SparqlQueryFactory getInstance() {
		if (instance == null)
			instance = new SparqlQueryFactory();
		return instance;
	}

	/**
	 * 
	 * @param keyword
	 * @return a query that once executed will return the offers corresponding
	 *         to a keyword
	 */
	public String getOffersQuery(String keyword) {
		queryBuilder.newQuery();
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR,
				SchemaConstants.PREFIX_GR_VALUE);
		queryBuilder.setSelectFields("ean", "provider", "name", "price");
		queryBuilder.addTypeCriterion("product", SchemaConstants.OFFER_CLASS);
		queryBuilder.addPropertyCriterion("product", SchemaConstants.MODEL_EAN,
				"ean");
		queryBuilder.addPropertyCriterion("product",
				SchemaConstants.OFFER_PROVIDER, "provider");
		queryBuilder.addPropertyCriterion("product",
				SchemaConstants.OFFER_PRICE, "price");
		queryBuilder.addPropertyCriterion("product",
				SchemaConstants.OFFER_NAME, "name");

		queryBuilder.addRegexFilter("name", "^" + keyword, "i");

		String query = queryBuilder.getQuery();

		return query;
	}
	
	/**
	 * 
	 * @param keyword
	 * @param providers
	 * @return a query that will return the offers corresponding to the keyword and available in at least one of the providers
	 */
	public String getOffersQuery(String keyword, List<BusinessEntity> providers) {
		if (providers == null || providers.isEmpty())
			return getOffersQuery(keyword);
		
		queryBuilder.newQuery();
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR, SchemaConstants.PREFIX_GR_VALUE);
		
		queryBuilder.setSelectFields("ean", "provider", "name", "price");
		
		queryBuilder.beginUnionClauses();
		for (int i = 0; i < providers.size(); i++) {
			queryBuilder.addTypeCriterion("product", SchemaConstants.OFFER_CLASS);
			queryBuilder.addPropertyCriterion("product", SchemaConstants.MODEL_EAN,
					"ean");
			queryBuilder.addPropertyCriterion("product",
					SchemaConstants.OFFER_PROVIDER, "provider");
			queryBuilder.addPropertyCriterion("product",
					SchemaConstants.OFFER_PRICE, "price");
			queryBuilder.addPropertyCriterion("product",
					SchemaConstants.OFFER_NAME, "name");
			queryBuilder.addRegexFilter("name", "^" + keyword, "i");
			queryBuilder.addRegexFilter("provider", providers.get(i).getLocation().get(0).getName(), "i");
			
			if (i != providers.size()-1)
				queryBuilder.addUnionClause();
			else
				queryBuilder.endUnionClauses();
		}
		
		return queryBuilder.getQuery();
	}

	/**
	 * 
	 * @param ean
	 *            : the 13 letters that is used to identify a product
	 * @return a query that once executed will return the offers having the
	 *         given ean
	 */

	public String getOffersByEanQuery(String ean) {
		queryBuilder.newQuery();
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR,
				SchemaConstants.PREFIX_GR_VALUE);
		queryBuilder.setSelectFields("ean", "provider", "name", "price");
		queryBuilder.addTypeCriterion("product", SchemaConstants.OFFER_CLASS);
		queryBuilder.addPropertyCriterion("product", SchemaConstants.MODEL_EAN,
				"ean");
		queryBuilder.addPropertyCriterion("product",
				SchemaConstants.OFFER_PROVIDER, "provider");
		queryBuilder.addPropertyCriterion("product",
				SchemaConstants.OFFER_PRICE, "price");
		queryBuilder.addPropertyCriterion("product",
				SchemaConstants.OFFER_NAME, "name");

		queryBuilder.addRegexFilter("ean", ean, "i");

		String query = queryBuilder.getQuery();

		return query;
	}
	
	/**
	 * 
	 * @param ean
	 * @param providers
	 * @return a query which will return the offers having the given ean code and available in at least one of the providers
	 */
	public String getOffersByEanQuery(String ean, List<BusinessEntity> providers) {
		if (providers == null || providers.isEmpty())
			return getOffersByEanQuery(ean);
		
		queryBuilder.newQuery();
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR, SchemaConstants.PREFIX_GR_VALUE);
		
		queryBuilder.setSelectFields("ean", "provider", "name", "price");
		
		queryBuilder.beginUnionClauses();
		for (int i = 0; i < providers.size(); i++) {
			queryBuilder.addTypeCriterion("product", SchemaConstants.OFFER_CLASS);
			queryBuilder.addPropertyCriterion("product", SchemaConstants.MODEL_EAN,
					"ean");
			queryBuilder.addPropertyCriterion("product",
					SchemaConstants.OFFER_PROVIDER, "provider");
			queryBuilder.addPropertyCriterion("product",
					SchemaConstants.OFFER_PRICE, "price");
			queryBuilder.addPropertyCriterion("product",
					SchemaConstants.OFFER_NAME, "name");
			queryBuilder.addRegexFilter("ean", ean, "i");
			queryBuilder.addRegexFilter("provider", providers.get(i).getLocation().get(0).getName(), "i");
			
			if (i != providers.size()-1)
				queryBuilder.addUnionClause();
			else
				queryBuilder.endUnionClauses();
		}
		
		return queryBuilder.getQuery();
	}

	/**
	 * 
	 * @param keyword
	 * @return a query that once executed will return the cheapest offer
	 *         matching a keyword
	 */
	public String getCheaperOfferQuery(String keyword) {		
		getOffersQuery(keyword);

		queryBuilder.setLimit(1);
		queryBuilder.setOrderBy("price", true);

		String query = queryBuilder.getQuery();

		return query;
	}

	/**
	 * 
	 * @param keyword
	 * @return a query that once executed will return the models matching a
	 *         keyword
	 */
	public String searchModels(String keyword) {
		queryBuilder.newQuery();
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR,
				SchemaConstants.PREFIX_GR_VALUE);
		queryBuilder.setSelectFields("ean", "name");

		queryBuilder.addTypeCriterion("product", SchemaConstants.OFFER_CLASS);

		queryBuilder.addPropertyCriterion("product", SchemaConstants.MODEL_EAN,
				"ean");
		queryBuilder.addPropertyCriterion("product",
				SchemaConstants.OFFER_NAME, "name");

		queryBuilder.addRegexFilter("name", "^" + keyword, "i");

		String query = queryBuilder.getQuery();
		return query;
	}

	/**
	 * retrieves the business entities of the database select ?legalName ?page
	 * where { ?company gr:BusinessEntity ?legalName. ?company gr:BusinessEntity
	 * ?page
	 * 
	 * select ?legalName where { ?company gr:BusinessEntity ?legalName. }
	 * @return the query
	 */
	public String makeSearchBusinessEntity() {
		queryBuilder.newQuery();
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR,
				SchemaConstants.PREFIX_GR_VALUE);

		queryBuilder.setSelectFields("legalName");

		queryBuilder.addTypeCriterion("company",
				SchemaConstants.BUSINESS_ENTITY_CLASS);
		queryBuilder.addPropertyCriterion("company",
				SchemaConstants.BUSINESS_ENTITY_LEGAL_NAME, "legalName");

		String query = queryBuilder.getQuery();

		return query;
	}

	/**
	 * retrieves the  page and logo's url from the database.
	 * @return the query
	 * 
	 * @see TestSparqlQueryBuilder
	 * 
	 * 
	 */
	public String makeSearchDetailsOfEntities() {
		// prefixes
		queryBuilder.newQuery();
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR,
				SchemaConstants.PREFIX_GR_VALUE);
		queryBuilder.addPrefix(SchemaConstants.PREFIX_FOAF,
				SchemaConstants.PREFIX_FOAF_VALUE);

		// fields to be selected
		queryBuilder.setSelectFields("depiction", "page");

		// equivalent to the where close
		queryBuilder.addTypeCriterion("details",
				SchemaConstants.BUSINESS_ENTITY_CLASS);
		queryBuilder.addPropertyCriterion("details",
				SchemaConstants.BUSINESS_ENTITY_LOGO, "depiction");
		queryBuilder.addPropertyCriterion("details",
				SchemaConstants.BUSINESS_ENTITY_PAGE, "page");

		return queryBuilder.getQuery();
	}

	/**
	 * retrieve the name, logo and page from db
	 * 
	 * @see TestSparqlQueryBuilder
	 * 
	 * @return Set of details. The name, the logo and the page
	 */
	public String retrieveAllInfosOnEntities() {

		queryBuilder.newQuery();

		// Prefixes
		queryBuilder.addPrefix(SchemaConstants.PREFIX_GR,
				SchemaConstants.PREFIX_GR_VALUE);
		queryBuilder.addPrefix(SchemaConstants.PREFIX_FOAF,
				SchemaConstants.PREFIX_FOAF_VALUE);

		queryBuilder.setSelectFields("legalName", "depiction", "page", "location");

		queryBuilder.addTypeCriterion("details",
				SchemaConstants.BUSINESS_ENTITY_CLASS);
		queryBuilder.addPropertyCriterion("details",
				SchemaConstants.BUSINESS_ENTITY_LEGAL_NAME, "legalName");
		queryBuilder.addPropertyCriterion("details",
				SchemaConstants.BUSINESS_ENTITY_LOGO, "depiction");
		queryBuilder.addPropertyCriterion("details",
				SchemaConstants.BUSINESS_ENTITY_PAGE, "page");
		queryBuilder.addPropertyCriterion("details", SchemaConstants.BUSINESS_ENTITY_LOCATION, "loc");
		queryBuilder.addPropertyCriterion("loc", SchemaConstants.LOCATION_NAME, "location");

		return queryBuilder.getQuery();
	}

}
